/**
 * 防抖和节流都是防止函数多次调用。
 * 区别：假设一个用户一直触发这个函数，且每次触发函数的间隔小于 wait，防抖的情况下只会调用一次，而节流的情况会每隔一定时间（参数 wait）调用函数。
 */

// 袖珍版的防抖理解一下防抖的实现
// func 是用户传入需要防抖的函数
// wait 是等待时间
const debounce = (func, wait = 50) => {
    // 缓存一个定时器 id
    let timer = 0
    // 这里返回的函数是每次用户实际调用的防抖函数
    // 如果已经设定过定时器了就清空上一次的定时器
    // 开始一个新的定时器,延迟执行用户传入的方法
    return function(...args) {

        if (timer) clearTimeout(timer)

        timer = setTimeout(() => {
            func.apply(this, args)
        }, wait);
    }
    // 不难看出如果用户调用该函数的间隔小于wait的情况下,上一次的时间未到就被清除了,并不会执行函数
}

// 这是简单版的防抖,但是有缺陷,这个防抖只能在最后调用.
// 一般的防抖会有 immediate 选项,表示是否立即调用.
// 下面实现一个带有立即执行选项的防抖函数.

// 这个是用来获取当前时间戳的
function now() {
    return +new Date()
}

/**
 * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait, func 才会执行
 *
 * @param  {function} func      回调函数
 * @param  {number}   wait      表示时间窗口的间隔
 * @param  {boolean}  immediate 设置为 true 时,是否立即调用函数
 * @return {function}           返回客户调用函数
 */
function debounce (func, wait = 50, immediate = true) {
    let timer, context, args

    // 延迟执行函数
    const later = () => setTimeout(() => {
        // 延迟函数执行完毕,清空缓存的定时器序号
        timer = null

        // 延迟执行的情况下,函数会在延迟函数中执行
        // 使用到之前缓存的参数和上下文
        if (!immediate) {
            func.apply(context, args)
            context = args = null
        }
    }, wait);

    // 这里返回的函数是每次实际调用的函数
    return function(...params) {
        // 如果没有创建延迟执行函数(later),就创建一个
        if (!timer) {
            timer = later()
            // 如果是立即执行,调用函数
            // 否则缓存参数和调用上下文
            if (immediate) {
                func.apply(this, params)
            } else {
                context = this
                args = params
            }
        } else {
            // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
            // 这样做延迟函数会重新计时
            clearTimeout(timer)
            timer = later()
        }
    }
}
/**
 * 整体函数实现的不难，总结一下。
 * 对于按钮防点击来说的实现：如果函数是立即执行的，就立即调用，
 * 如果函数是延迟执行的，就缓存上下文和参数，放到延迟函数中去执行。
 * 一旦我开始一个定时器，只要我定时器还在，你每次点击我都重新计时。
 * 一旦你点累了，定时器时间到，定时器重置为 null，就可以再次点击了。
 * 对于延时执行函数来说的实现：清除定时器ID，如果是延迟调用就调用函数
 */